class Block {
    constructor(
        width = 20,
        height = 20,
        inactiveColor = "#899573",
        activatedColor = "#000"
    ) {
        this.width = width;
        this.height = height;

        this.inactiveColor = inactiveColor;
        this.activatedColor = activatedColor;
    }
    /**
     * 绘制格子
     * @param {Element} ctx canvas对象的Context
     * @param {Number} row 行坐标
     * @param {Number} col 列坐标     *
     * @param {*} activated false未活动格子,true激活的格子
     * @param {Number} offset 偏移大小
     */
    draw(ctx, row, col, activated = false, offset = 4) {
        const x = col * (this.width + 2) + offset;
        const y = row * (this.height + 2) + offset;

        let color = this.inactiveColor;
        if (activated) {
            color = this.activatedColor;
        }
        ctx.beginPath();
        ctx.rect(x, y, this.width, this.height);
        ctx.strokeStyle = color;
        ctx.stroke();
        ctx.closePath();
        // 格子内部
        ctx.beginPath();
        ctx.rect(x + 4, y + 4, this.width - 8, this.height - 8);
        ctx.fillStyle = color;
        ctx.fill();
        ctx.closePath();
    }

    /**
     * 格子闪烁一下
     * @param {*} ctx canvas对象的Context
     * @param {number} msTime 时间间隔,默认500ms
     */
    flash(ctx, row, col, msTime = 500) {
        this.draw(ctx, row, col, false);
        setTimeout(() => {
            this.draw(ctx, row, col, true);
        }, msTime);
    }
}

class TetrisShapes {
    Shapes = [
        [
            [
                [0, 0, 1],
                [1, 1, 1],
            ],
            [
                [1, 0],
                [1, 0],
                [1, 1],
            ],
            [
                [1, 1, 1],
                [1, 0, 0],
            ],
            [
                [1, 1],
                [0, 1],
                [0, 1],
            ],
        ], // L
        [
            [
                [1, 0, 0],
                [1, 1, 1],
            ],
            [
                [1, 1],
                [1, 0],
                [1, 0],
            ],
            [
                [1, 1, 1],
                [0, 0, 1],
            ],
            [
                [0, 1],
                [0, 1],
                [1, 1],
            ],
        ], //反L
        [
            [
                [1, 1, 0],
                [0, 1, 1],
            ],
            [
                [0, 1],
                [1, 1],
                [1, 0],
            ],
            [
                [1, 1, 0],
                [0, 1, 1],
            ],
            [
                [0, 1],
                [1, 1],
                [1, 0],
            ],
        ], //Z
        [
            [
                [0, 1, 1],
                [1, 1, 0],
            ],
            [
                [1, 0],
                [1, 1],
                [0, 1],
            ],
            [
                [0, 1, 1],
                [1, 1, 0],
            ],
            [
                [1, 0],
                [1, 1],
                [0, 1],
            ],
        ], //反Z
        [
            [
                [0, 1, 0],
                [1, 1, 1],
            ],
            [
                [1, 0],
                [1, 1],
                [1, 0],
            ],
            [
                [1, 1, 1],
                [0, 1, 0],
            ],
            [
                [0, 1],
                [1, 1],
                [0, 1],
            ],
        ], //T
        [
            [
                [1, 1],
                [1, 1],
            ],
            [
                [1, 1],
                [1, 1],
            ],
            [
                [1, 1],
                [1, 1],
            ],
            [
                [1, 1],
                [1, 1],
            ],
        ], //田
        [
            [[1, 1, 1, 1]],
            [[1], [1], [1], [1]],
            [[1, 1, 1, 1]],
            [[1], [1], [1], [1]],
        ], //I
        [
            [
                [1, 0, 1],
                [1, 1, 1],
            ],
            [
                [1, 1],
                [1, 0],
                [1, 1],
            ],
            [
                [1, 1, 1],
                [1, 0, 1],
            ],
            [
                [1, 1],
                [0, 1],
                [1, 1],
            ],
        ], //凹
    ];
    constructor(arrGrid, maxRows = 20, maxCols = 10, dropTime = 1000) {
        const intShapeidx = parseInt(Math.random() * this.Shapes.length);
        const transIdx = parseInt(Math.random() * 4);

        this.arrGrid = arrGrid;
        this.direction = transIdx;
        this.shapeIndex = intShapeidx;
        this.shapeArray = this.Shapes[intShapeidx][transIdx];
        this.maxRows = maxRows;
        this.maxCols = maxCols;
        this.dropTime = dropTime;

        this.row = -1;
        this.col = parseInt(
            (maxCols - this.shapeArray[this.shapeArray.length - 1].length) / 2
        );

        //
        this.timerDropDone = 0;
        this.isDone = false;
        this.isPause=false;

        // console.log("TetrisBlock:",this.arrGrid);
    }
    start() {
        clearInterval(this.timerDrop);
        this.timerDrop = setInterval(this.drop.bind(this), this.dropTime);
        this.isPause=false;
    }
    gameOver() {
        clearInterval(this.timerDrop);
    }
    pause() {
        clearInterval(this.timerDrop);
        this.isPause=true;
    }
    /**
     * 图形变形,按顺时针方向
     *
     */
    transformBlock() {
        if(this.isPause){return;}
        const direction = (this.direction + 1) % 4;
        // 检查是否有空间变形
        const shapeArray = this.Shapes[this.shapeIndex][direction];
        const col = this.col + shapeArray[shapeArray.length - 1].length - 1;
        if (col >= this.maxCols) {
            // 空间不足,不变形
            return;
        }
        for (let r = shapeArray.length - 1; r >= 0; r--) {
            for (let c = 0; c < shapeArray[r].length; c++) {
                if (shapeArray[r][c] == 1) {
                    let row = this.row - (shapeArray.length - 1) + r;
                    if (row >= 0 && row < this.maxRows) {
                        if (this.arrGrid[row][this.col + c] == 1) {
                            // 变形后与现有冲突, 不变形
                            return;
                        }
                    }
                }
            }
        }
        this.shapeArray = this.Shapes[this.shapeIndex][direction];
        this.direction = direction;
    }

    /**
     * 向左移动
     *
     */
    moveLeft() {
        // 检查左边是否可以移动
        if(this.isPause){return;}

        for (let i = this.shapeArray.length - 1; i >= 0; i--) {
            const row = this.row - (this.shapeArray.length - 1) + i;
            for (let c = 0; c < this.shapeArray[i].length; c++) {
                if (row >= 0 && this.col + c - 1 >= 0) {
                    if (
                        this.shapeArray[i][c] == 1 &&
                        this.arrGrid[row][this.col + c - 1] == 1
                    ) {
                        return;
                    }
                }
            }
        }
        if (this.col > 0) {
            this.col--;
        }
        clearTimeout(this.timerDropDone);
    }
    /**
     * 向右移动     *
     */
    moveRight() {
        if(this.isPause){return;}
        // 检查右边是否可以移动
        let col = 0;
        for (let i = this.shapeArray.length - 1; i >= 0; i--) {
            const row = this.row - (this.shapeArray.length - 1) + i;
            for (let c = this.shapeArray[i].length - 1; c >= 0; c--) {
                col = this.col + c + 1;

                if (row >= 0 && col < this.maxCols) {
                    if (
                        this.shapeArray[i][c] == 1 &&
                        this.arrGrid[row][col] == 1
                    ) {
                        return;
                    }
                }
            }
        }
        if (this.col + this.shapeArray[0].length < this.maxCols) {
            this.col++;
        }
        clearTimeout(this.timerDropDone);
    }

    fastDrop() {
        if(this.isPause){return;}
        clearInterval(this.timerDrop);
        for (let i = 0; i < 20; i++) {
            if (!this.drop()) {
                break;
            }
        }
    }

    /**
     * 自动下落
     *
     */
    drop() {
        // 检查下面是否可以移动
        if(this.isPause){return;}
        
        for (let r = this.shapeArray.length - 1; r >= 0; r--) {
            const row = this.row - (this.shapeArray.length - 1) + r + 1;

            if (row < 0) {
                break;
            }

            for (let i = 0; i < this.shapeArray[0].length; i++) {
                if (row < this.maxRows) {
                    if (
                        this.shapeArray[r][i] == 1 &&
                        this.arrGrid[row][this.col + i] == 1
                    ) {
                        clearTimeout(this.timerDropDone);
                        this.timerDropDone = setTimeout(
                            this.dropDone.bind(this),
                            100
                        );
                        return false;
                    }
                } else {
                    clearTimeout(this.timerDropDone);
                    this.timerDropDone = setTimeout(
                        this.dropDone.bind(this),
                        100
                    );
                    return false;
                }
            }
        }

        this.row++;
        return true;
    }

    /**
     * 下落完成, 图形写入格子数组
     *
     */
    dropDone() {
        clearInterval(this.timerDrop);
        clearTimeout(this.timerDropDone);

        for (let i = this.shapeArray.length - 1; i >= 0; i--) {
            for (let c = 0; c < this.shapeArray[0].length; c++) {
                const row = this.row - (this.shapeArray.length - 1) + i;
                if (this.shapeArray[i][c] == 1) {
                    if (row >= 0) {
                        this.arrGrid[row][this.col + c] = 1;
                    }
                }
            }
        }
        this.isDone = true;
    }
}

class Tetris {
    constructor(maxRows, maxCols, canvas) {
        this.maxRows = maxRows;
        this.maxCols = maxCols;
        this.canvas = canvas;
        this.ctx = canvas.getContext("2d");

        // this.init();
    }

    init() {
        this.arrGrid = [];

        for (let i = 0; i < this.maxRows; i++) {
            const rows = [];
            for (let c = 0; c < this.maxCols; c++) {
                rows.push(0);
            }
            this.arrGrid.push(rows);
        }

        // console.log(this.arrGrid);
        this.timerDrop = 1000;
        this.currentShape = new TetrisShapes(
            this.arrGrid,
            this.maxRows,
            this.maxCols,
            this.timerDrop
        );
        this.nextShape = new TetrisShapes(
            this.arrGrid,
            this.maxRows,
            this.maxCols,
            this.timerDrop
        );
        this.block = new Block();

        this.score = 0;
        this.level = 1;
        this.cleans = 0;

        this.isPause = false;
        this.timeRender = 0;
        this.isGameOver = false;
    }

    drawBackground() {
        // 背影
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.ctx.beginPath();
        this.ctx.fillStyle = "#9ead86";
        this.ctx.rect(0, 0, this.canvas.width, this.canvas.height);
        this.ctx.fill();
        this.ctx.strokeStyle = "#000";
        this.ctx.stroke();
        this.ctx.closePath();

        try {
            for (let i = 0; i < this.arrGrid.length; i++) {
                for (let c = 0; c < this.arrGrid[0].length; c++) {
                    this.block.draw(this.ctx, i, c, false);
                }
            }
        } catch (error) {}
    }
    /**
     * 开始游戏
     */
    start() {
        this.init();
        this.drawBackground();
        this.render.bind(this)();
        // clearInterval(this.timeRender);
        // this.timeRender = setInterval(this.render.bind(this), 20);
        this.currentShape.start();
    }
    /**
     * 暂停游戏
     */
    pause() {
        if (this.isGameOver) {
            this.start();
        } else {
            this.isPause = !this.isPause;
            if (this.isPause) {
                // clearInterval(this.timeRender);
                this.currentShape.pause();
            } else {
                // clearInterval(this.timeRender);
                // this.timeRender = setInterval(this.render.bind(this), 20);
                this.currentShape.start();
            }
        }
    }
    /**
     * Game Over动画及文字
     */
    drawGameOver() {
        this.ctx.beginPath();
        this.ctx.fillStyle = "#000";
        this.ctx.font = "35px Arial";
        this.ctx.fillText("Game Over", 25, 230);
    }

    /**
     * 画分数
     */
    drawUI() {
        this.ctx.beginPath();
        this.ctx.clearRect(230, 30, 65, 400);
        this.ctx.fillStyle = "#9ead86";
        this.ctx.rect(230, 30, 65, 400);
        this.ctx.fill();
        this.ctx.fillStyle = "#000";
        this.ctx.font = "16px Arial";
        // Score
        this.ctx.fillText("Score", 230, 50);
        this.ctx.fillText(this.score.toString().padStart(7, "0"), 230, 70);
        // clearn Lines
        this.ctx.fillText("Cleans", 230, 120);
        this.ctx.fillText(this.cleans.toString().padStart(7, " "), 230, 140);
        // Level
        this.ctx.fillText("Level", 230, 190);
        this.ctx.fillText(this.level.toString().padStart(7, " "), 230, 210);
        // Next shape
        this.ctx.fillText("Next", 230, 260);
        for (let r = 0; r < this.nextShape.shapeArray.length; r++) {
            for (let c = 0; c < this.nextShape.shapeArray[0].length; c++) {
                if (this.nextShape.shapeArray[r][c] == 1) {
                    this.block.draw(this.ctx, 12 + r, 10 + c, true, 10);
                } else {
                    this.block.draw(this.ctx, 12 + r, 10 + c, false, 10);
                }
            }
        }

        //暂停状态
        this.ctx.beginPath();
        this.ctx.fillStyle = "#000";
        if (this.isPause) {
            //暂停
            this.ctx.rect(230, 380, 5, 20);
            this.ctx.fill();

            this.ctx.rect(240, 380, 5, 20);
            this.ctx.fill();
            this.ctx.fillText("Pause", 250, 395);
        } else {
            this.ctx.moveTo(230, 380);
            this.ctx.lineTo(240, 390);
            this.ctx.lineTo(230, 400);
            this.ctx.closePath();
            this.ctx.fill();
            this.ctx.fillText("Play", 250, 395);
        }
        //时间
        const time = new Date();
        this.ctx.fillText(time.toLocaleTimeString(), 230, 425);
    }

    gameOver() {
        // clearInterval(this.timeRender);
        // cancelAnimationFrame(this.timeRender);
        this.currentShape.gameOver();
        // console.log("Game Over");
        this.isGameOver = true;

        const that = this;
        for (let i = this.arrGrid.length - 1; i >= 0; i--) {
            for (let c = 0; c < this.arrGrid[0].length; c++) {
                setTimeout(function () {
                    that.block.draw(that.ctx, i, c, true);
                }, 100);
            }
        }

        setTimeout(function () {
            for (let i = 0; i < that.arrGrid.length; i++) {
                for (let c = 0; c < that.arrGrid[0].length; c++) {
                    setTimeout(function () {
                        that.block.draw(that.ctx, i, c, false);
                    }, 100);
                }
            }
        }, 500);

        setTimeout(() => {
            // that.drawBackground();
            that.drawGameOver();
        }, 1000);
    }

    moveLeft() {
        this.currentShape.moveLeft();
    }

    moveRight() {
        this.currentShape.moveRight();
    }

    moveDown() {
        this.currentShape.drop();
    }

    moveFastDown() {
        this.currentShape.fastDrop();
    }

    rotate() {
        this.currentShape.transformBlock();
    }

    render() {
        if (!this.isGameOver) {
            this.drawBackground();

            //画已经落地格子
            for (let r = 0; r < this.arrGrid.length; r++) {
                for (let c = 0; c < this.arrGrid[0].length; c++) {
                    if (this.arrGrid[r][c] == 1) {
                        this.block.draw(this.ctx, r, c, true);
                    }
                }
            }
            //画图形
            for (let r = this.currentShape.shapeArray.length - 1; r >= 0; r--) {
                const row =
                    this.currentShape.row -
                    (this.currentShape.shapeArray.length - 1) +
                    r;

                if (r >= 0) {
                    for (
                        let c = 0;
                        c < this.currentShape.shapeArray[0].length;
                        c++
                    ) {
                        if (this.currentShape.shapeArray[r][c] == 1) {
                            this.block.draw(
                                this.ctx,
                                row,
                                this.currentShape.col + c,
                                true
                            );
                        }
                    }
                }
            }

            if (this.currentShape.isDone) {
                this.checkLines.bind(this)();
                this.currentShape = this.nextShape;
                this.nextShape = new TetrisShapes(
                    this.arrGrid,
                    this.maxRows,
                    this.maxCols,
                    this.dropTime
                );
                this.currentShape.start();
            }
        }
        this.drawUI();
        this.timeRender = requestAnimationFrame(this.render.bind(this));
    }
    /**
     * 检查行数是否已经圆满
     */
    checkLines() {
        //clearInterval(this.timeRender);
        let lines = 0;
        for (let r = this.arrGrid.length - 1; r >= 0; r--) {
            const line = this.arrGrid[r].join("");
            if (!line.includes("0", 0)) {
                // 不包含0则说明此行已满
                const newRow = [];
                for (let c = 0; c < this.arrGrid[0].length; c++) {
                    this.block.flash(this.ctx, r, c, 100); //闪烁
                    newRow.push(0); // 生成一个新行
                }

                this.arrGrid.splice(r, 1); //删除当前行

                this.arrGrid.unshift(newRow); //从0添加一个空行

                this.cleans++;
                lines++;
                r++;
            }
        }
        switch (lines) {
            case 1:
                this.score += 100;
                break;
            case 2:
                this.score += 300;
                break;
            case 3:
                this.score += 600;
                break;
            case 4:
                this.score += 800;
                break;
            default:
                break;
        }

        // 根据分数调整等级
        this.level = parseInt(this.score / 2000) + 1;
        if (this.level > 1) {
            if (this.dropTime <= 100) {
                this.dropTime = 100;
            } else {
                this.dropTime = 1000 - this.level * 50;
            }
        }
        // this.timeRender = setInterval(this.render.bind(this), 20);
        if (this.currentShape.row <= 0) {
            this.gameOver();
        }
    }
}

export { Tetris };
